/** 
 *  Copyright 2010 Matthew Werny & Burk Price.
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package net.iceflow.db.migration;

import java.util.Hashtable;
import java.util.Map;

import org.apache.commons.io.FilenameUtils;
import org.springframework.core.io.Resource;

import net.iceflow.db.migration.executer.GroovyMigrationExecuter;
import net.iceflow.db.migration.executer.SQLScriptMigrationExecuter;
import net.iceflow.db.migration.exception.MigrationException;

/**
 * @version 1.0
 */
public class MigrationFactory {

   // TODO - Move the private interface and implementations out into separate source files
   // TODO - Reconsider the life-cycle of the migration executor so that they would only need to be instantiated once.
   
   /**
    * Factory delegates that do the actual instantiation and initialization work for the MigrationExecuter.
    */
   private final Map<String, MigrationFactoryDelegate> delegates = new Hashtable<String, MigrationFactoryDelegate>();

   /**
    * Constructs a new MigrationFactory without external initialization parameters.
    */
   public MigrationFactory() {
      // Populate the delegate map with some reasonable defaults
      this.addDelegate(new SQLMigrationFactoryDelegate());
      this.addDelegate(new GroovyMigrationFactoryDelegate());
   }

   public MigrationExecuter create(String version, Resource resource) {

      MigrationExecuter executer = null;

      final String extension = FilenameUtils.getExtension(resource.getFilename()).toLowerCase();

      try {
         executer = this.delegates.get(extension).create(version, resource);
      } catch  (NullPointerException npe) {
         throw new MigrationException("Can't determine migration type for " + resource);
      }

      return executer;
   }

   public void addDelegate(MigrationFactoryDelegate delegate) {
      this.delegates.put(delegate.getDefaultExtension(), delegate);
   }

   public interface MigrationFactoryDelegate {

      /**
       * Gets the default extension for which the factory is associated.
       * @return Extension
       */
      String getDefaultExtension();

      /**
       * Creates and returns a factory delegate
       * @param version Version of the migration
       * @param resource Source resource for the file
       * @return Created MigrationExecuter instance
       */
      MigrationExecuter create(String version, Resource resource);
   }

   /**
    * Factory delegate for SQL migrations.
    */
   public class SQLMigrationFactoryDelegate implements MigrationFactoryDelegate {

      public MigrationExecuter create(String version, Resource resource) {
         return new SQLScriptMigrationExecuter(version, resource);
      }

      public String getDefaultExtension() {
         return "sql";
      }
   }

   /**
    * Factory delegate for SQL migrations.
    */
   public class GroovyMigrationFactoryDelegate implements MigrationFactoryDelegate {

      public MigrationExecuter create(String version, Resource resource) {
         return new GroovyMigrationExecuter(version, resource);
      }

      public String getDefaultExtension() {
         return "groovy";
      }
   }
}
